代码逆向基础
关于逆向工程
分析方法:
静态分析与动态分析,二者结合相辅相成。
- 静态看类型、PE文件信息、壳信息、字符串、API + IDA Pro源码
- 动态看行为监控 + 动态调试
先静态-推测程序结构与行为机制,再动态-找切入点,如DLL的main、关键API调用等。
两个概念:
- patch:打补丁,即对应用程序文件或者进程内存进行修改
- Crack:破解,与Patch类似,但是意图非法
前者目的在于修复漏洞、后者在于破解软件;而二者应用的技术就是逆向工程。而学习的内容是逆向技术原理与OS内部体系结构。
简单逆向分析
以OD为例,简单实例分析。
入口点:
当使用OD打开可执行文件后,会跳转到EP位置进行执行。需要注意的是这个地址不是main函数,而某些程序的启动代码,这些代码随着编译器的不同而变化。
回顾一下基础的指令:
Ctrl+G
:地址的跳转,包括代码窗口与内存窗口F2
:断点(软)Ctrl+F2
:重新加载调试F4
:运行到指定位置。利用的是硬件断点。F7
:单步步入F8
:单步步过F9
:多步步过(到下一个断点)Ctrl+F9
:执行到返回:
:添加标签(修改替换);
:添加注释*
:回到EIPEnter
:查看call、jmp后续ALT+B
:查看断点窗口ALT+L
:查看日志窗口Ctrl+E
:数据窗口快速编辑
下面介绍几种快速定点的方法:
所谓的快速定点就是在调试过程中添加人为标记,便于整个流程分析与复盘。
Ctrl+G
ANDF4
上面的指令都有涉及到,前者用于地址的跳转,后者用于执行到光标位置。所以二者结合可以实现在某个位置开始调试。
设置断点(后续具体介绍,是最常用的一种调试手段)
断点的种类很多,比如
硬件断点、软件断点、内存断点、条件断点
等,设置断点的方法也很多,比如在某位置使用F2
或者通过Ctrl+N
利用导入表设置;使用ALT+B
可以打开断点窗口,查看或跳转到某断点。添加注释:
通过使用
;
可以在指定位置添加注释,而后使用右键查找-用户注释
即可查看。设置标签:
可以通过
:
设置标签,即在指定地址添加特定的名称。也可以在上图位置进行查看设置的所有标签。
下面介绍几种快速查找指定代码的方法:以查找messageBox所在的main函数为例。
极限F8法:
适用场景:功能明确,代码简单
具体方法:确定大本营后,连续使用F8,直到跳出窗口,则该函数就是main函数,可以设置标记(断点、标签等)
字符串检索法:
OD在加载文件时会先进行预分析,将使用到的字符串、API等单独列出,使用可以通过这种方法查询指定字符串。如下图,通过查看
helloworld
字符串即可定位到main函数。API检索法:
通过查看该程序的函数调用列表,找到messageBox函数。
模块与导入函数法:
通过右键-查找-所有模块,之后通过名称查询即可。
前面介绍了如何设置定位点、如何查找指定API,下面学习在找到指定代码后如何进行修改。
直接修改内存内容。
通过
Ctrl+G
跳转到指定的内存区域,使用Ctrl+E
进行编辑。注意要加00结尾。修改传递参数。
新建一个字符串,之后修改
messageBox
的参数即可,使用空格修改汇编代码。
字节端序
什么是字节序呢?简单来说就是多字节数据在内存或者网络中的排序方式。
可以分为两种字节序:
- 大端序:内存低位地址存储数据的高位,多是网络协议中使用。
- 小端序:内存低地址存储数据的低位,多少Intel x86CPU使用,也是后面学习使用的。
怎么记忆呢?记住一种即可:高对高逆序存储 = 契合 = 广泛 = 小端。
IA-32寄存器学习
调试过程中涉及的寄存器很多,在不断学习中不断总结。本次学习基本程序运行寄存器。
其他寄存器:内存寄存器、调试寄存器、控制寄存器,非IA-32架构的寄存器,后续学习都会涉及。
简单分类:
- 通用寄存器:用于数据的存储与传输。一共8个:
eax、ebx、ecx、edx、esp、ebp、esi、edi
- 段寄存器:用于存储段描述符,与SDT一起完成虚拟内存的构建与段机制的完成。一共6个,16位:
CS、DS、SS、ES、FS、GS
。 - 标志寄存器:用于标识状态信息,32位、按位起作用。
- 指令指针寄存器:存储下一条指令的地址,32位、EIP。

细致介绍:
通用寄存器:
eax、ebx、ecx、edx、esp、ebp、esi、edi
概况:最常用的几个寄存器,要熟悉其用法。
具体功能介绍:
EAX
:累加器add。多用于保存函数返回结果。EBX
:基址寄存器base。多用于记录数据基址。ECX
:计数器count。多用于计数,比如循环中使用其保存i。EDX
:数据寄存器data。血统最纯,记录重要数据。ESP
:栈顶指针寄存器,保存栈顶指针,很重要,在函数调用、栈帧机制中。EBP
:扩展基址指针寄存器,用于SS表示的栈区,记录某个栈帧的基址。ESI
:源地址寄存器,多用于内存复制。EDI
:目的指针寄存器,多用于内存复制。
段寄存器:
CS、SS、DS、ES、FS、GS
概况:16位寄存器,存储的不是段的地址,而是SDT的索引。(SDT是段描述符表,下面有介绍)
具体介绍:
CS
:代码段寄存器SS
:栈段寄存器DS
:数据段寄存器ES
:附加段寄存器FS
:数据段寄存器,这个寄存器很重要,后续学习SEH(异常处理机制)
、TEB、PEB(环境块)
时会用到。GS
:数据段寄存器
扩展:段描述符表(Segment Descriptor Table)
段描述符表(Segment Descriptor Table)是指在x86架构的计算机体系结构中使用的数据结构,用于管理和描述内存中的各个段(segment)。
在x86架构中,内存被划分为多个段,每个段具有不同的权限和属性。段描述符表存储了这些段的描述符,每个描述符包含了
段的基地址(base address)
、段的大小(segment size)
、访问权限(access rights)
和其他属性信息
。 段描述符表通常是一个数组或者表格的形式,每个表项对应一个段描述符。处理器使用
段选择子(Segment Selector)
来索引段描述符表,从而找到对应的段描述符。当程序引用一个内存地址时,处理器会根据段选择子找到对应的段描述符,然后使用其中的信息来执行相应的访问控制和内存管理操作。 需要注意的是,自从Intel引入了64位的x86架构(x86-64或AMD64),段描述符表的使用已经不再是必须的,因为64位模式下的内存管理采用了平坦模型(
Flat Model
),即所有的内存被视为单一的连续空间,不再划分为不同的段。然而,段描述符表仍然在兼容性模式下使用,以支持32位的应用程序和操作系统。补图:
标志寄存器:
概况:这个寄存器用于记录状态信息,是以bit为单位的。
功能:不需要记住所有的标志位含义,只需要知道其中的三个。
ZF
:zero flag,零标志位,若该寄存器为1,则表示结果为0.OF
:overflow flag,溢出标志位,若该值为1,则表示有符号整数溢出。CF
:Carry flag,进位标志位,若该值为1,则表示无符号整数溢出。
歌曰:标志位寄存器经常与条件跳转关联,比如
JNZ、JZ
等,根据ZF标志位的结果决定是否跳转;而在判断之前会通过test、cmp
等指令为标志位赋值。指令指针寄存器:EIP,存储下一条指令的地址。
栈与栈帧
理清关键概念定义,用自己的话。
当曰:什么是栈?
歌曰:栈是一种数据结构,遵循先入后出的数据存储原则。具体到虚拟内存空间,栈是其中一段连续的地址,向着低地址方向增长,通过PUSH
和POP
实现压栈和弹栈。
当曰:那栈有什么用嘞?
歌曰:那当然有用。栈与程序的执行过程密切相关,因为程序就是各个函数的组合,而每一个函数的状态变化便是通过栈记录的。具体来说,栈有以下三个作用:
- 参数的传递:在函数执行之前,会先传递参数,以供函数内部调用。(64位程序采取寄存器传参)
- 状态的记录(上下文信息):在函数执行完毕后要返回,所以要知道函数执行前的状态信息,比如关键寄存器的数据、要返回的地址等。
- 局部变量的存储:局部变量只有在函数内部起作用,如何实现呢,只需要在栈里创建该变量即可,不需要在数据段。
当曰:什么是栈帧?
歌曰:栈帧可以看作是栈区一小段空间,每一个栈帧都对应一个函数调用。当函数调用时要先开辟栈帧,由EBP
指向栈帧底部、ESP
指向栈帧顶部。栈帧的作用就是管理函数调用过程中的参数、局部变量、返回值、上下文信息。
当曰:如何管理呢?
歌曰:程序利用EBP寄存器
访问栈内局部变量、参数、函数返回地址等信息。通过使用栈帧,计算机可以有效地管理函数调用和返回的过程,确保函数之间的数据隔离和正确的执行顺序。
函数调用约定
函数调用约定就是指函数在指向过程中,
参数如何被调用
的约定。具体来说,无外乎两个方面,即参数如何传递、函数结束后参数如何销毁。
在介绍具体的调用约定之前,先来说一下当函数执行完毕后栈里的数据怎么处理?答案就是:不处理。因为处理是需要花费资源的,所以一般的做法是只改变EBP和ESP的值,即修改栈的大小;后续使用栈时直接覆盖即可。
下面具体说下目前使用最多的三种调用约定:
cdecl
:C语言默认的调用约定,由调用者进行栈平衡操作,参数由栈进行传递。这种方法的好处是可以传递可变长度的参数,更加灵活,其栈平衡操作通常是调用者使用add esp,xx
进行。stdcall
:WIN32 API默认使用的调用约定,由被调用者进行栈平衡操作,参数由栈进行传递。这种方法的好处代码尺寸小,其栈平衡操作通过是使用RETN
完成的,即ret与 pop N
。fastcall
:与stdcall
类似,不同的是前面几个参数使用寄存器传递,所以执行速度更快。
后续学习
切记不要急躁,切记不要蜻蜓点水,切记不要眼高手低。
各个工具的使用、分析流程的梳理、PE文件格式与加载流程、进程注入与Hook、Windows内核与驱动、反汇编反调试反虚拟机反沙箱、脱壳与加壳、补丁与保护机制等等,每一个都不是只看看理论就可以掌握的,要细看、要书画、要思考、要实践、要总结、要记录、要回顾、要更新。
恶意样本中基本分析流程与细致分析方法、各个类型家族实践总结、WinAPI\zfc\dll\reg积累、常见的入侵行为如感染、启动、隐蔽、持久化、核心功能点、样本处置措施三个,对于这些更要多多实践分析,学无止境。
漏洞研究先看基础、内核,再看书、实践复现等。